﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace System.UnitTesting
{
    public static class ExceptionAssert
    {
        public static void HasDefaultMessage(Exception exception)
        {
            Assert.IsNotNull(exception);

            // Exception of type '[typename]' was thrown
            StringAssert.Contains(exception.Message, "'" + exception.GetType().FullName + "'");
        }

        public static T Throws<T, TInner>(Action action)
            where T : Exception
            where TInner : Exception
        {
            T exception = Throws<T>(action);

            Type expectedType = typeof(TInner);

            if (exception.InnerException == null)
                Assert.Fail("Expected {0} be the inner exception, however, it is null.", expectedType);

            Type actualType = exception.InnerException.GetType();

            Assert.AreSame(expectedType, actualType, "Expected {0} to be the inner exception, however, {1} is.", expectedType, actualType);

            return exception;
        }

        public static T Throws<T>(Exception innerException, Action action)
            where T : Exception
        {
            T exception = Throws<T>(action);

            Assert.AreSame(innerException, exception.InnerException, "Expected {0} to be the inner exception, however, {1} is.", innerException, exception.InnerException);

            return exception;
        }

        public static void Throws<T>(Action action, int retryCount)
            where T : Exception
        {
            for (int i = 0; i < retryCount; i++)
            {
                Throws<T>(action);
            }
        }

        public static T Throws<T>(Action action)
            where T : Exception
        {
            Type expectedType = typeof(T);

            Exception actual = Run(action);
            if (actual == null)
                Assert.Fail("Expected {0} to be thrown", expectedType);

            Type actualType = actual.GetType();

            Assert.AreSame(expectedType, actualType, "Expected {0} to be thrown, however, {1} was thrown.", expectedType, actualType);

            return (T)actual;
        }

        public static void Throws(Exception expected, Action action)
        {
            Exception actual = Run(action);

            Assert.AreSame(expected, actual, "Expected {0} to be thrown, however, {1} was thrown.", expected, actual);
        }

        private static Exception Run(Action action)
        {
            try
            {
                action();
                return null;
            }
            catch (Exception ex)
            {
                return ex;
            }

        }
    }
}
